/**
 * @mainpage Mine Sweeper Clone
 *
 * \image html snapshot.png
 *
 * Il campo di gioco consiste in un campo rettangolare (o quadrato) a sua volta
 * suddiviso in tanti piccoli quadrati. Ogni quadrato viene ripulito, o
 * scoperto, cliccando su di esso. Se un quadrato contiene una mina, quando
 * sarà cliccato la mina esploderà e farà terminare il gioco. Se il quadrato
 * non contiene una mina, possono accadere due eventi. Se appare un numero esso
 * indica la quantità di quadrati adiacenti (inclusi quelli in diagonale) che
 * contengono mine. Se non appare nessun numero, in questo caso il gioco
 * ripulisce automaticamente i quadrati adiacenti a quello vuoto (fino a quando
 * non conterrano un numero). Si vince la partita quando tutti i quadrati che
 * non contengono mine saranno scoperti.
 *
 * Il giocatore può segnare ogni quadrato in cui crede che sia presente una
 * mina con una bandiera, cliccando su di esso con il tasto destro.
 *
 * <div align=right><i>tratto da wikipedia</i></div>
 *
 * <br><hr><br>
 *
 * <b>Linee di massima di funzionamento</b>
 *
 * All'avvio del programma, viene invocata la funziona ::main che si occupa
 * principalmente di inizializzare i widgets delle varie finestre e di creare
 * una prima griglia 9x9 (::create_new_grid). \n
 * A questo punto il programma resta in attesa che venga premuta una qualsiasi
 * casella della griglia che scaturisce l'evento ::on_button_press_event.
 * In questo evento:
 * * se il tasto premuto è quello destro, viene chiamata la funzione
 *   ::button_left_click che cambia ciclicamente lo stato della casella in
 *   ::BS_NORMAL (casella normale), ::BS_CHECKED (casella contrassegnata come
 *   contenente una mina) e ::BS_MAYBE (casella contrassegnata come
 *   probabilmente contenente una mina)
 * * se il tasto premuto è quello sinistro, viene chiamata la funzione
 *   ::button_right_click che rivela il contenuto della casella (solo nel caso
 *   in cui la casella non abbia uno stato ::BS_CHECKED) ed eventualmente
 *   anche delle caselle adiacenti (::button_reveal).
 *
 * \n
 *
 * <b>Informazioni strutturali</b>
 *
 * Il programma è stato suddiviso nei seguenti moduli:
 * * common.h: vengono definiti i parametri condizionali di compilazione, i
 *   percorsi delle risorse neccessarie al corretto funzionamento del software e
 *   le funzioni comuni
 * * debug.h: vengono gestite le funzioni e le macro per il debug
 * * grid_data.h: viene definita la struttura portante ::grid_s che gestisce i
 *   dati principali della griglia e le relative caselle
 * * records_io.h: gestisce il caricamento/salvataggio dei records del gioco
 * * xpm_images.h: vengono gestiti i files di immagini che verranno caricati, e
 *   resi disponibili al programma
 * * xpm_images_list.h: viene gestita la lista di immagini da includere
 *   nell'eseguibile
 *
 * La funzione ::main è contenuta nel modulo minesweeperclone.cc.
 *
 * \n
 *
 * \note Si sono riscontrati degli errori nell'apertura del file .glade negli
 * ambienti con Glade 3.14.2 e Xubuntu 13.10.\n
 * E' un bug noto che si presenta quando si tenta di aprire un file .glade
 * contenente delle dialog. \n
 * Una soluzione testata è l'aggiornamento non ufficiale di Glade tramite i
 * seguenti comandi: \n
 * \n
 * \code{.cpp}
 * sudo add-apt-repository ppa:jfi/test
 * sudo apt-get update
 * sudo apt-get upgrade
 * \endcode
 *
 * \n \n
 *
 * @author Adrian Sassatelli
 *
 * @file
 * Modulo contenente principalmente la funzione ::main, la gestione degli eventi
 * dei widgets utilizzati dalla finestra principale e la gestione del timer
 * della partita.
 */

// Author: Adrian Sassatelli
// Copyright (c) 2014 Adrian Sassatelli


#include <iostream>
#include <stdlib.h>
#include <gtk/gtk.h>
#include <time.h>
#include "common.h"
#include "grid_data.h"
#include "records_io.h"
#include "xpm_images.h"
#include "debug.h"

using namespace std;


/* -------------------------------------------------------------------------- */
/*    DICHIARAZIONI                                                           */
/* -------------------------------------------------------------------------- */

// Dichiarazione dei puntatori ai widgets della finestra informazioni
/**
 * Struttura contenente i controlli della finestra informazioni.
 */
struct about_dialog_s {
    GtkWidget *window;         /**< Puntatore alla finestra */
};

/* -------------------------------------------------------------------------- */

/**
 * Struttura contenente i controlli della finestra principale.
 */
struct main_form_s {
	GtkWidget *window;         /**< Puntatore alla finestra */

    GtkWidget *wgrid;          /**< Puntatore alla griglia principale */
    GtkWidget *wtimer_000x;    /**< Puntatore all'immagine delle unità dei secondi del contatore tempo */
    GtkWidget *wtimer_00x0;    /**< Puntatore all'immagine delle decine dei secondi del contatore tempo */
    GtkWidget *wtimer_sep;     /**< Puntatore all'immagine del separatore del contatore tempo */
    GtkWidget *wtimer_0x00;    /**< Puntatore all'immagine delle unità dei minuti del contatore tempo */
    GtkWidget *wtimer_x000;    /**< Puntatore all'immagine delle decine dei minuti del contatore tempo */

    GtkWidget *wmines_000x;    /**< Puntatore all'immagine delle unità del numero totale di mine */
    GtkWidget *wmines_00x0;    /**< Puntatore all'immagine delle decine del numero totale di mine */
    GtkWidget *wmines_sep;     /**< Puntatore all'immagine del separatore del numero totale di mine */
    GtkWidget *wmines_0x00;    /**< Puntatore all'immagine delle unità del numero totale di mine segnate */
    GtkWidget *wmines_x000;    /**< Puntatore all'immagine delle decine del numero totale di mine segnate */

    GtkWidget *wlblrecord;     /**< Puntatore alla label per la visualizzazione del record attuale */
    GtkWidget *wstatus;        /**< Puntatore al buttone dello stato della partita */
};

/* -------------------------------------------------------------------------- */

/**
 * Struttura contenente i controlli della finestra per l'inserimento del nome
 * del giocatore
 */
struct name_dialog_s {
    GtkWidget *window;         /**< Puntatore alla finestra */

    GtkWidget *wentry;          /**< Puntatore alla casella di testo per inserimento nome giocatore */
};

/* -------------------------------------------------------------------------- */

/**
 * Struttura contenente i controlli della finestra per l'inserimento delle
 * dimensioni per la creazione di una griglia personalizzata
 */
struct customgrid_dialog_s {
    GtkWidget *window;         /**< Puntatore alla finestra */

    GtkWidget *wspin_rows;     /**< Puntatore alla casella per inserimento numero righe */
    GtkWidget *wspin_cols;     /**< Puntatore alla casella per inserimento numero colonne */
    GtkWidget *wspin_mines;    /**< Puntatore alla casella per inserimento numero mine */
};


/* -------------------------------------------------------------------------- */
/*    DICHIARAZIONE VARIABILI                                                 */
/* -------------------------------------------------------------------------- */

// Dichiarazioni finestre
static about_dialog_s about_dialog;            /**< Struttura contenente i controlli della finestra informazioni */
static main_form_s main_form;                  /**< Struttura contenente i controlli della finestra principale */
static name_dialog_s name_dialog;              /**< Struttura contenente i controlli della finestra per l'inserimento del nome del giocatore */
static customgrid_dialog_s customgrid_dialog;  /**< Struttura contenente i controlli della finestra per l'inserimento del nome del giocatore */

/* -------------------------------------------------------------------------- */

// Dichiarazioni esterne
extern grid_s grid;    // Struttura dati principale contenente tutte le singole
                       // celle ed i relativi status (definita in common.h)

int MASK = 1 | 2 | 4;    // Maschera per abilitare i vari livelli di debug

/* -------------------------------------------------------------------------- */

// Altre dichiarazioni
static guint timer_id;               /**< ID dell'evento timer */
static int timer_count;              /**< Tempo trascorso di gioco */
static record_data_s record_data;    /**< Dati relativi al record di gioco attuale */


/* -------------------------------------------------------------------------- */
/*    PROTOTIPAZIONE                                                          */
/* -------------------------------------------------------------------------- */

void create_new_grid (const int acols, const int arows, const int anummines);
MODULE_EXPORT gboolean on_button_press_event (GtkWidget *widget, GdkEvent *event, gpointer user_data);
MODULE_EXPORT void on_button_status_clicked (GtkButton *button, gpointer user_data);
MODULE_EXPORT gboolean on_mainwindow_delete_event (GtkWidget *widget, GdkEvent *event, gpointer user_data);
MODULE_EXPORT void on_menuitem_about_activate (GtkMenuItem *menuitem, gpointer user_data);
MODULE_EXPORT void on_menuitem_newcustom_activate (GtkMenuItem *menuitem, gpointer user_data);
MODULE_EXPORT void on_menuitem_new16x16_activate (GtkMenuItem *menuitem, gpointer user_data);
MODULE_EXPORT void on_menuitem_new30x16_activate (GtkMenuItem *menuitem, gpointer user_data);
MODULE_EXPORT void on_menuitem_new9x9_activate (GtkMenuItem *menuitem, gpointer user_data);
MODULE_EXPORT void on_menuitem_quit_activate (GtkMenuItem *menuitem, gpointer user_data);
void show_record (const int acols, const int arows, const int anummines);
gboolean timer_handler_event (gpointer user_data);
void timer_handler_start ();
void timer_handler_stop ();
void update_window_widgets ();


/* -------------------------------------------------------------------------- */
/*    MAIN (seguono funzioni)                                                 */
/* -------------------------------------------------------------------------- */

/**
 *
 * MAIN
 *
 * Il compito principale di questa funzione è quello di:
 * * rendere disponibili le immagini incluse nel file eseguibile (si veda
 *   xpm_images.h)
 * * caricare il file glade dove vengono definite le finestre ed i relativi
 *   widgets
 * * impostare i puntatori ai vari widgets utilizzati ed agganciarne gli
 *   eventuali eventi
 * * creare una prima griglia 9x9
 */
int main (int argc, char *argv[]) {
    DBG_FUNCNAME ("main", "");

    // Sezione inizializzazione componenti principali
    srand (time (NULL));    // Inizializza generatore numeri casuali
    gtk_init (&argc, &argv);    // Inizializza gtk+
    load_xpm_images ();    // Caricamento immagini principali

    // Sezione inizializzazione widgets
    GtkBuilder *builder = gtk_builder_new ();
    if (gtk_builder_add_from_file (builder, MAIN_GLADE_FILE, NULL) == 0) {
        // Errore mentre cerco di aprire il file glade
        DBG_GENERIC ("problema all'apertura del file glade");

        error_dialog (g_strdup_printf ("Non riesco ad aprire il file %s!", MAIN_GLADE_FILE));

        return -1;
    };
    gtk_builder_connect_signals (builder, NULL);    // Collega tutti i segnali

    // Widgets di main_form
    main_form.window = (GtkWidget *) gtk_builder_get_object (builder, "mainwindow");    // Widget window principale
    main_form.wgrid = (GtkWidget *) gtk_builder_get_object (builder, "maingrid");    // Widget griglia principale

    main_form.wtimer_000x = (GtkWidget *) gtk_builder_get_object (builder, "image_timer_000x");   // Image unità secondi timer
    main_form.wtimer_00x0 = (GtkWidget *) gtk_builder_get_object (builder, "image_timer_00x0");   // Image decine secondi timer
    main_form.wtimer_sep = (GtkWidget *) gtk_builder_get_object (builder, "image_timer_sep");   // Image separatore timer
    main_form.wtimer_0x00 = (GtkWidget *) gtk_builder_get_object (builder, "image_timer_0x00");   // Image unità minuti timer
    main_form.wtimer_x000 = (GtkWidget *) gtk_builder_get_object (builder, "image_timer_x000");   // Image decine minuti timer

    main_form.wmines_000x = (GtkWidget *) gtk_builder_get_object (builder, "image_mines_000x");   // Image unità num mine
    main_form.wmines_00x0 = (GtkWidget *) gtk_builder_get_object (builder, "image_mines_00x0");   // Image decine num mine
    main_form.wmines_sep = (GtkWidget *) gtk_builder_get_object (builder, "image_mines_sep");   // Image separatore num mine segnate
    main_form.wmines_0x00 = (GtkWidget *) gtk_builder_get_object (builder, "image_mines_0x00");   // Image unità num mine segnate
    main_form.wmines_x000 = (GtkWidget *) gtk_builder_get_object (builder, "image_mines_x000");   // Image decine num mine segnate

    main_form.wlblrecord = (GtkWidget *) gtk_builder_get_object (builder, "label_record");    // Label per visualizzazione del record attuale
    main_form.wstatus = (GtkWidget *) gtk_builder_get_object (builder, "button_status");   // Bottone stato di gioco

    // Widgets di about_dialog
    about_dialog.window = (GtkWidget *) gtk_builder_get_object (builder, "about_dialog");    // Dialog informazioni

    // Widgets di name_dialog
    name_dialog.window = (GtkWidget *) gtk_builder_get_object (builder, "name_dialog");    // Dialog nome giocatore
    name_dialog.wentry = (GtkWidget *) gtk_builder_get_object (builder, "entry_name");    // Casella di testo per inserimento nome giocatore

    // Widgets di customgrid_dialog
    customgrid_dialog.window = (GtkWidget *) gtk_builder_get_object (builder, "customgrid_dialog");    // Dialog griglia personalizzata
    customgrid_dialog.wspin_rows = (GtkWidget *) gtk_builder_get_object (builder, "spin_rows");    // Casella per inserimento numero righe
    customgrid_dialog.wspin_cols = (GtkWidget *) gtk_builder_get_object (builder, "spin_cols");    // Casella per inserimento numero colonne
    customgrid_dialog.wspin_mines = (GtkWidget *) gtk_builder_get_object (builder, "spin_mines");    // Casella per inserimento numero mine

    g_object_unref (builder);

    // Sezione sistemazioni widgets
    image_set_from_pix ((GtkImage *) main_form.wmines_sep, PI_NUMBER_SEP1);    // Imposta immagine separatore num mine
    image_set_from_pix ((GtkImage *) main_form.wtimer_sep, PI_NUMBER_SEP2);    // Imposta immagine separatore timer

    // Sezione inizializzazione strutture a variabili
    grid_init ();    // Impostazioni iniziali griglia
    on_menuitem_new9x9_activate (NULL, NULL);    // Crea griglia iniziale

    // Fase conclusiva creazione finestra
    gtk_widget_show_all (main_form.window);
    gtk_main ();

    // Free dei componenti
    grid_delete ();
    unload_xpm_images ();

    return 0;
}

/* -------------------------------------------------------------------------- */
/*    FUNZIONI                                                                */
/* -------------------------------------------------------------------------- */

/**
 * Crea una nuova griglia sulla finestra.
 *
 * Inoltre viene creato il collegamento della casella all'evento
 * ::on_button_press_event passando come parametro dell'evento il puntatore alla
 * struttura ::button_s relativa a quella casella.
 *
 * @param[in] acols Numero di colonne della griglia (MIN 1)
 * @param[in] arows Numero di righe della griglia (MIN 1)
 * @param[in] anummines Numero di mine (MAX arows * acols - 1)
 */
void create_new_grid (const int acols, const int arows, const int anummines) {
    DBG_FUNCNAME ("create_new_grid", "crea una nuova griglia sulla finestra");

    // Sistemazione timer
    DBG_BLOCDESC ("sistemazione timer");
    if (timer_id != 0) timer_handler_stop ();    // Fermo l'eventuale timer avviato
    timer_count = -1;    // Resetto il contatore del tempo trascorso
    timer_handler_event (NULL);    // Aggiorno i componenti

    // Crea griglia
    grid_fill (acols, arows, anummines, main_form.wgrid, G_CALLBACK (on_button_press_event));

    // Imposta finestra
    DBG_BLOCDESC ("aggiornamento componenti finestra");
    show_record (acols, arows, anummines);    // Visualizza dati record corrente
    gtk_window_resize ((GtkWindow *) main_form.window, 20, 20);    // Resize della window per visualizzare meglio la griglia
    update_window_widgets ();    // Aggiorna i componenti di stato sulla finestra
    gtk_widget_show_all (main_form.wgrid);
}

/* -------------------------------------------------------------------------- */

/**
 * Evento invocato quando viene cliccata la casella.
 *
 * In base a quale casella è stata cliccata, viene visualizzato il contenuto o
 * cambiato lo stato della casella.
 *
 * @param[in] widget Oggetto che ha ricevuto il segnale
 * @param[in] event Evento che ha scaturito il segnale
 * @param[in] user_data Dati utente impostati quando il segnale è stato
 *            collegato
 * @return true per fermare la propagazione del segnale e false per continuarla
 */
MODULE_EXPORT gboolean on_button_press_event (GtkWidget *widget, GdkEvent *event, gpointer user_data) {
    DBG_FUNCNAME ("on_button_press_event", "gestione click sulla casella");

    if (event->type == GDK_BUTTON_PRESS) {    // Controlla che l'evento sia un click
        if (grid.game_status != GS_NORMAL) return true;    // Partita già finita, esci senza propagare il segnale

        // Faccio partire il timer
        if (timer_id == 0) {
            DBG_GENERIC ("faccio partire il timer");
            timer_handler_start ();
        }

        // Sezione per check cosa devo fare
        button_s *widget = (button_s *) user_data;    // Riprendi i dati passati dall'evento

        int col = widget->coln;
        int row = widget->rown;

        switch (event->button.button) {
            case 1:    // Premuto tasto sinistro
                DBG_BLOCDESC ("click sinistro su casella " << col << "-" << row);

                button_left_click (col, row);

                break;
            case 3:    // Premuto tasto destro
                DBG_BLOCDESC ("click destro su casella " << col << "-" << row);

                button_right_click (col, row);    // Cambia stato alla casella

                break;
            default: ;
        }

        // Aggiornamento componenti stato gioco
        grid_check_win ();    // Controlla se la partita è stata vinta
        if (grid.game_status != GS_NORMAL) {    // La partita è finita in qualche modo
            DBG_GENERIC ("partita finita");
            timer_handler_stop ();    // Ferma timer

            // Controlla se la partita è stata vinta con record
            if ((grid.game_status == GS_WIN) && ((record_data.seconds > timer_count) || (record_data.name == NULL))) {    // Partita vinta e nuovo record
                DBG_GENERIC ("partita vinta e nuovo record");

                if (record_data.name == NULL) {
                    gtk_entry_set_text ((GtkEntry *) name_dialog.wentry, "");    // Imposta testo nome corrente
                } else {
                    gtk_entry_set_text ((GtkEntry *) name_dialog.wentry, record_data.name);    // Imposta testo nome corrente
                }

                if (gtk_dialog_run ((GtkDialog *) name_dialog.window) == 0) {    // Visualizza dialog richiesta nome
                    // Premuto tasto OK
                    string entry_line = gtk_entry_get_text ((GtkEntry *) name_dialog.wentry);

                    if ((entry_line.length () != 0) && (entry_line != "")) {    // Controllo esattezza valori
                        DBG_GENERIC ("dati coerenti, salvo");

                        record_data.name = g_strdup (entry_line.c_str ());
                        record_data.seconds = timer_count;
                        if (save_current_record (record_data, grid.cols, grid.rows, grid.nummines) == false) {    // Salva record corrente
                            error_dialog (g_strdup_printf ("Errore nella scrittura del file %s!", RECORDS_INI_FILE));
                        }

                        show_record (grid.cols, grid.rows, grid.nummines);    // Ricarico dati salvati così controllo che tutto sia ok
                    }
                }
                gtk_widget_hide (name_dialog.window);    // Nascondo finestra
            }
        }
        update_window_widgets ();    // Aggiorna i componenti di stato sulla finestra
    }

    return false;    // Esci e propaga il segnale
}

/* -------------------------------------------------------------------------- */

/**
 * Evento invocato quando viene premuto il pulsante dello stato della partita.
 *
 * Viene iniziata una nuova partita con le dimensione della griglia attuale.
 *
 * @param[in] button Oggetto che ha ricevuto il segnale
 * @param[in] user_data Dati utente impostati quando il segnale è stato
 *            collegato
 */
MODULE_EXPORT void on_button_status_clicked (GtkButton *button, gpointer user_data) {
    DBG_FUNCNAME ("on_button_status_clicked", "inizio nuova partita con proprietà giglia attuale");

    create_new_grid (grid.cols, grid.rows, grid.nummines);    // Inizia nuova partita
}

/* -------------------------------------------------------------------------- */

/**
 * Evento invocato quando viene richiesta la chiusura della finestra.
 *
 * @param[in] widget Oggetto che ha ricevuto il segnale
 * @param[in] event Evento che ha scaturito il segnale
 * @param[in] user_data Dati utente impostati quando il segnale è stato
 *            collegato
 * @return true per fermare la propagazione del segnale e false per continuarla
 */
MODULE_EXPORT gboolean on_mainwindow_delete_event (GtkWidget *widget, GdkEvent *event, gpointer user_data) {
    DBG_FUNCNAME ("on_mainwindow_delete_event", "richiesta di chiusura del programma");

    gtk_main_quit ();

    return true;
}

/* -------------------------------------------------------------------------- */

/**
 * Visualizza dialog con informazioni sul programma.
 *
 * @param[in] menuitem Oggetto che ha ricevuto il segnale
 * @param[in] user_data Dati utente impostati quando il segnale è stato
 *            collegato
 */
MODULE_EXPORT void on_menuitem_about_activate (GtkMenuItem *menuitem, gpointer user_data) {
    DBG_FUNCNAME ("on_menuitem_about_activate", "visualizza about dialog");

    gtk_dialog_run ((GtkDialog *) about_dialog.window);
    gtk_widget_hide (about_dialog.window);
}

/* -------------------------------------------------------------------------- */

/**
 * Crea una griglia personalizzata.
 *
 * @param[in] menuitem Oggetto che ha ricevuto il segnale
 * @param[in] user_data Dati utente impostati quando il segnale è stato
 *            collegato
 */
MODULE_EXPORT void on_menuitem_newcustom_activate (GtkMenuItem *menuitem, gpointer user_data) {
    DBG_FUNCNAME ("on_menuitem_newcustom_activate", "crea nuova griglia personalizzata");

	if (gtk_dialog_run ((GtkDialog *) customgrid_dialog.window) == 0) {    // Visualizza dialog creazione griglia personalizzata
		DBG_BLOCDESC ("lettura valori e controllo coerenza dati");

		// Lettura valori inseriti
		int c = gtk_spin_button_get_value_as_int ((GtkSpinButton *) customgrid_dialog.wspin_cols);
		int r = gtk_spin_button_get_value_as_int ((GtkSpinButton *) customgrid_dialog.wspin_rows);
		int m = gtk_spin_button_get_value_as_int ((GtkSpinButton *) customgrid_dialog.wspin_mines);

		// Controllo coerenza dati
		if (m >= r * c) {
			DBG_GENERIC ("errore nell'inserimento dei dati");
			error_dialog (g_strdup_printf ("Il numero di mine deve essere minore di %d", r * c));
		} else {
			DBG_GENERIC ("dati ok");
			create_new_grid (c, r, m);    // Crea nuova griglia personalizzata
		}
	}
    gtk_widget_hide (customgrid_dialog.window);
}

/* -------------------------------------------------------------------------- */

/**
 * Crea una griglia di dimensione 16x16 con 40 mine (livello intermedio).
 *
 * @param[in] menuitem Oggetto che ha ricevuto il segnale
 * @param[in] user_data Dati utente impostati quando il segnale è stato
 *            collegato
 */
MODULE_EXPORT void on_menuitem_new16x16_activate (GtkMenuItem *menuitem, gpointer user_data) {
    DBG_FUNCNAME ("on_menuitem_new16x16_activate", "crea nuova griglia livello intermedio");

    create_new_grid (16, 16, 40);    // Crea nuova griglia livello intermedio
}

/* -------------------------------------------------------------------------- */

/**
 * Crea una griglia di dimensione 30x16 con 99 mine (livello esperto).
 *
 * @param[in] menuitem Oggetto che ha ricevuto il segnale
 * @param[in] user_data Dati utente impostati quando il segnale è stato
 *            collegato
 */
MODULE_EXPORT void on_menuitem_new30x16_activate (GtkMenuItem *menuitem, gpointer user_data) {
    DBG_FUNCNAME ("on_menuitem_new30x16_activate", "crea nuova griglia livello esperto");

    create_new_grid (30, 16, 99);    // Crea nuova griglia livello esperto
}

/* -------------------------------------------------------------------------- */

/**
 * Crea una griglia di dimensione 9x9 con 10 mine (livello principiante).
 *
 * @param[in] menuitem Oggetto che ha ricevuto il segnale
 * @param[in] user_data Dati utente impostati quando il segnale è stato
 *            collegato
 */
MODULE_EXPORT void on_menuitem_new9x9_activate (GtkMenuItem *menuitem, gpointer user_data) {
    DBG_FUNCNAME ("on_menuitem_new9x9_activate", "crea nuova griglia livello principiante");

    create_new_grid (9, 9, 10);    // Crea griglia livello principiante
}

/* -------------------------------------------------------------------------- */

/**
 * Chiude programma.
 *
 * @param[in] menuitem Oggetto che ha ricevuto il segnale
 * @param[in] user_data Dati utente impostati quando il segnale è stato
 *            collegato
 */
MODULE_EXPORT void on_menuitem_quit_activate (GtkMenuItem *menuitem, gpointer user_data) {
    DBG_FUNCNAME ("on_menuitem_quit_activate", "esci dal programma");

    gtk_main_quit ();
}

/* -------------------------------------------------------------------------- */

/* Visualizza dati record corrente */
/**
 * Visualizza i dati del record corrente.
 *
 * La funzione carica i dati del record dal file ::RECORDS_INI_FILE e li
 * visualizza sulla finestra principale.
 *
 * @param[in] acols Numero di colonne della griglia
 * @param[in] arows Numero di righe della griglia
 * @param[in] anummines Numero di mine
 */
void show_record (const int acols, const int arows, const int anummines) {
    // Lettura dati eventuale record precedente
    load_current_record (&record_data, acols, arows, anummines);    // Lettura dati record
    if (record_data.name == NULL) {
		DBG_GENERIC ("nessun record disponibile");
        gtk_label_set_text ((GtkLabel *) main_form.wlblrecord, "NESSUNO");
    } else {
        // Calcolo e visualizzo il tempo trascorso
        int imin = int (record_data.seconds / 60);
        int isec = record_data.seconds - imin * 60;

        gchar *v = g_strdup_printf ("%02d:%02d  -  %s", imin, isec, record_data.name);
		DBG_GENERIC ("record " << v);
        gtk_label_set_text ((GtkLabel *) main_form.wlblrecord, v);
        g_free (v);
    }
}

/* -------------------------------------------------------------------------- */

/**
 * Evento contatore tempo gioco.
 *
 * L'evento viene invocato ogni secondo dal sistema e visualizza il tempo
 * passato dall'inizio della partita.
 *
 * @param[in] user_data Dati utente impostati quando il segnale è stato
 *            collegato
 * @return true per continuare con l'esecuzione ciclica dell'evento e false per
 *         fermarlo
 */
gboolean timer_handler_event (gpointer user_data) {
//    DBG_FUNCNAME ("timer_handler_event", "evento timer");

    // Aumento contatore se non ha raggiunto il limite superiore
    if (timer_count < MAX_COUNTED_TIME) {    // Conto fino al limite impostato in common.h
        timer_count++;    // Aumento contatore timer
    } else {
        timer_handler_stop ();    // Fermo il tempo
    }

    // Calcolo e visualizzo il tempo trascorso
    int imin = int (timer_count / 60);
    int isec = timer_count - imin * 60;

    int ix0 = int (isec / 10);
    int i0x = isec - ix0 * 10;
    show_image_number ((GtkImage *) main_form.wtimer_000x, i0x);
    show_image_number ((GtkImage *) main_form.wtimer_00x0, ix0);

    ix0 = int (imin / 10);
    i0x = imin - ix0 * 10;
    show_image_number ((GtkImage *) main_form.wtimer_0x00, i0x);
    show_image_number ((GtkImage *) main_form.wtimer_x000, ix0);

    return true;
}

/* -------------------------------------------------------------------------- */

/**
 * Avvia il contatore del tempo di gioco trascorso.
 *
 * Avvia il timer impostando l'invio del segnale all'evento timer ogni 1000ms.
 */
void timer_handler_start () {
    DBG_FUNCNAME ("timer_handler_start", "avvia il timer");

    assert (timer_id == 0);    // Controllo che il timer sia fermo

    timer_id = g_timeout_add (1000, (GSourceFunc) timer_handler_event, NULL);    // Avvio timer. Evento ogni 1s
}

/* -------------------------------------------------------------------------- */

/**
 * Ferma il contatore del tempo di gioco trascorso.
 */
void timer_handler_stop () {
    DBG_FUNCNAME ("timer_handler_stop", "ferma il timer");

    assert (timer_id != 0);    // Controllo che il timer sia avviato

    g_source_remove (timer_id);    // Fermo timer
    timer_id = 0;
}

/* -------------------------------------------------------------------------- */

/**
 * Aggiorna i componenti di stato sulla finestra.
 *
 * In particolare:
 * * il contatore delle mine
 * * lo stato della partita
 */
void update_window_widgets () {
    DBG_FUNCNAME ("update_window_widgets", "aggiorna componenti stato sulla finestra");

    // Visualizzo numero mine rimaste
    show_image_number ((GtkImage *) main_form.wmines_000x, int (grid.nummines % 10));
    show_image_number ((GtkImage *) main_form.wmines_00x0, int (grid.nummines / 10));

    show_image_number ((GtkImage *) main_form.wmines_0x00, int (grid.selmines % 10));
    show_image_number ((GtkImage *) main_form.wmines_x000, int (grid.selmines / 10));

    // Visualizzo stato partita
    pix_image_e v = PI_NONE;
    switch (grid.game_status) {
        case GS_LOST: v = PI_STATUS_LOST; break;
        case GS_NORMAL: v = PI_STATUS_NORMAL; break;
        case GS_WIN: v = PI_STATUS_WIN; break;
        default: assert (false);    // Non possibile
    }
    gtk_button_set_image ((GtkButton *) main_form.wstatus, (GtkWidget *) image_new_from_pix (v));    // Imposta immagine
}

/* -------------------------------------------------------------------------- */
